package com.sfac.modules.common.controller;


import com.sfac.modules.common.pojo.ExceptionLog;
import com.sfac.modules.common.service.ExceptionLogService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author:唐琳的爹
 * @BelongsProject:hqyj_springboot
 * @BelongsPackage:com.sfac.modules.common.controller
 * @CreateTime:2022/8/25 10:34:11
 */
@ControllerAdvice // 捕获所有控制器的异常进行处理
/**
 * ModelAndView可以实现返回页面或者JSON数据
 */
public class ExceptionController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);

    @Autowired
    private ExceptionLogService exceptionLogService;

    /**
     * 捕获控制层所有的异常，根据异常类型和调用类型来决定返回对象
     * 接口调用异常返回403,404,500对应的JSON数据
     * 页面调用则返回403,404,500对应的页面
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public ModelAndView handleException(HttpServletRequest request ,Exception e){

        e.printStackTrace();

    // 调用记录异常信息接口，将异常信息存入数据库
        insertExceptionLog(request,e);
    // 根据异常类型，封装数据
        int code = 200;
        String message;
        String data;
    // 没有权限
    if (e instanceof UnauthorizedException){
        code = 403;
        message = "没有权限访问";
        data = "/common/403";
    }else {
        code = 500;
        message = "网络出现了问题，请稍后重试";
        data = "/common/500";
    }

    // 判断请求类型，接口调用返回JSON数据,反之返回页面
        if (checkCallType(request)){
            ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
            modelAndView.addObject("status",code);
            modelAndView.addObject("message",message);
            modelAndView.addObject("data",data);
            return modelAndView;
        }else {
            return new ModelAndView(new RedirectView(data));
        }
    }

    /**
     * 判断是否为接口请求方式
     * @param request 将request作参数传进来
     * @return
     */
    private boolean checkCallType(HttpServletRequest request){

        HandlerMethod handlerMethod = (HandlerMethod)request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        // 从方法上获取@ResponseBody注解
        ResponseBody[] annotations1 = handlerMethod.getMethod().getAnnotationsByType(ResponseBody.class);
        // 从类上获取@ResponseBody和@RestController注解
        ResponseBody[] annotations2 = handlerMethod.getBeanType().getAnnotationsByType(ResponseBody.class);
        RestController[] annotations3 = handlerMethod.getBeanType().getAnnotationsByType(RestController.class);
        // 如果存在任何一个注解则是接口方法,返回True
        return (annotations1.length > 0)||(annotations2.length > 0)||(annotations3.length >0);
    }


    /**
     * 用于存储异常
     */
    private void insertExceptionLog(
            HttpServletRequest request,Exception e
    ){
        ExceptionLog exceptionLog = new ExceptionLog();
        // 获取请求的远程ip地址
        exceptionLog.setIp(request.getRemoteAddr());
        exceptionLog.setPath(request.getServletPath());

        HandlerMethod handlerMethod = (HandlerMethod)request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        exceptionLog.setClassName(handlerMethod.getBeanType().getName());
        exceptionLog.setMethodName(handlerMethod.getMethod().getName());

        exceptionLog.setExceptionType(e.getClass().getSimpleName());
        exceptionLog.setExceptionMessage(StringUtils.isNoneBlank(
                e.getMessage())?e.getMessage().length() > 200?e.getMessage().substring(0,200):e.getMessage():"");

        exceptionLogService.insertExceptionLog(exceptionLog);
    }
}
